/* eslint-disable jsx-a11y/no-noninteractive-tabindex */
/* eslint-disable jsx-a11y/tabindex-no-positive */
import React, { useEffect, useImperativeHandle, useReducer, useRef } from 'react';
import PropTypes from 'prop-types';
import UseKey from 'react-use/lib/comps/UseKey';
import classNames from 'classnames';
import { useInterval } from '@/hooks';
import Portal from './Portal';
import GuideContext from './GuideContext';
import styles from './index.module.less';

/**
 * 检查单个元素的样式级别的是否可见
 * @param ele
 * @returns {boolean}
 */
function elementIsVisible(ele) {
    const style = window.getComputedStyle(ele);
    return (
        style.width.toString() !== '0' &&
        style.height.toString() !== '0' &&
        style.opacity.toString() !== '0' &&
        style.display !== 'none' &&
        style.visibility !== 'hidden'
    );
}

/**
 * 向上检查, 判断元素的父级是否有被隐藏的
 * @param el
 * @returns {*|boolean}
 */
function closestCheckElementIsVisible(el) {
    if (elementIsVisible(el)) {
        return el.parentElement ? closestCheckElementIsVisible(el.parentElement) : true;
    }
    return false;
}

const reducer = (state, action) => {
    const nextStepIndex = state.currentStepIndex + 1;
    const { payload } = action;
    let currentSteps = [...state.steps];

    switch (action.type) {
        case 'start':
            if (state.onStart) {
                state.onStart(state);
            }
            // 支持指定位置开始
            return {
                ...state,
                active: true,
                currentStepIndex: payload?.index ? payload.index : state.currentStepIndex
            };
        case 'stop':
            if (state.showOnce && state.localStorageKey) {
                window.localStorage.setItem(state.localStorageKey, 'true');
            }
            if (state.onStop) {
                state.onStop(state);
            }
            return { ...state, active: false };
        case 'reset':
            return { ...state, active: false, currentStepIndex: 0 };
        case 'register':
            // 支持指定顺序注册新组件
            currentSteps.push({
                el: payload.el,
                index: payload.index === null || payload.index === undefined ? 999 : payload.index
            });
            // 重新排序
            currentSteps = currentSteps.sort((a, b) => a.index - b.index);
            return { ...state, steps: currentSteps };
        case 'unRegister':
            return { ...state, steps: state.steps.filter(step => step.el !== payload.el) };
        case 'next':
            // 没有下一步直接结束
            if (nextStepIndex >= state.steps.length) {
                if (state.showOnce && state.localStorageKey) {
                    window.localStorage.setItem(state.localStorageKey, 'true');
                }
                if (state.onStop) {
                    state.onStop(state);
                }
                return { ...state, active: false, currentStepIndex: 0 };
            }
            if (state.onNext) {
                state.onNext(state);
            }
            return { ...state, active: true, currentStepIndex: nextStepIndex };
        case 'toggle':
            // 切换开始接受状态
            return {
                ...state,
                active: payload?.active !== undefined ? payload.active : !state.active
            };
        case 'toggleVisible':
            // 切换显示状态
            return {
                ...state,
                visible: payload?.visible !== undefined ? payload.visible : !state.visible
            };
        default:
            return state;
    }
};

/**
 * 引导(下一步下一步)组件
 */
const Guide = React.forwardRef((props, ref) => {
    const {
        autoStart,
        showOnce,
        overlay,
        zIndex,
        overlayClickNext,
        localStorageKey,
        allowEsc,
        finishText,
        nextText,
        skipText,
        isShowSkipBtn,
        children,
        onStart,
        onNext,
        onStop
    } = props;
    const [state, dispatch] = useReducer(reducer, {
        currentStepIndex: 0,
        active: false,
        visible: true,
        steps: [],
        showOnce,
        // 和当前项目业务绑定, 根据当前用户做缓存
        localStorageKey: `${localStorageKey}-${JSON.parse(localStorage.user || '{}')?.employeeCode || ''}`,
        zIndex,
        overlay,
        finishText,
        nextText,
        skipText,
        isShowSkipBtn,
        onStart,
        onNext,
        onStop
    });
    const guideRef = useRef();

    // 控制显示状态
    // 存在一种情况, 显示引导的情况下, 这个页面被隐藏了(切选项卡的情况), 但是节点还在, 导致引导错位.
    // 加个时钟判断, 引导组件是否可见, 如果被意外隐藏了, 就停止显示, 切换回来的时候再显示.
    const [start, clear] = useInterval(
        () => {
            if (guideRef.current) {
                const isVisible = closestCheckElementIsVisible(guideRef.current);
                if (state.visible !== isVisible) {
                    dispatch({ type: 'toggleVisible', payload: { visible: isVisible } });
                }
            }
        },
        1000,
        { immediate: true, manual: true }
    );

    if (state.active) {
        start();
    } else if (!state.active) {
        clear();
    }

    // 参数检查
    useEffect(() => {
        if (showOnce && !localStorageKey) {
            console.warn('设置了 showOnce, 必须同时设置 localStorageKey 参数');
        }
    }, [localStorageKey, showOnce]);

    // 自动开始
    useEffect(() => {
        if (autoStart) {
            // 是否满足自动开始的条件
            const preventShow = showOnce && state.localStorageKey && window.localStorage.getItem(state.localStorageKey);
            if (!preventShow && state.steps?.length) {
                dispatch({ type: 'start' });
            }
        }
    }, [autoStart, showOnce, state.localStorageKey, state.steps]);

    // 暴露方法
    useImperativeHandle(ref, () => ({
        start: () => {
            const isVisible = guideRef.current ? closestCheckElementIsVisible(guideRef.current) : false;
            if (isVisible) {
                dispatch({ type: 'start' });
            }
        },
        stop: () => {
            dispatch({ type: 'stop' });
        }
    }));

    // 点击遮罩层执行下一步
    const handleOverlayClick = () => {
        if (overlayClickNext) {
            dispatch({ type: 'next' });
        }
    };

    const isShow = state.active && state.visible;

    return (
        <GuideContext.Provider value={{ state, dispatch }}>
            <div ref={guideRef}>
                {children}
                {isShow && !!state.steps.length && allowEsc && (
                    // esc
                    <UseKey filter={event => event.keyCode === 27} fn={() => dispatch({ type: 'next' })} />
                )}
                {isShow && !!state.steps.length && (
                    <Portal>
                        <div
                            tabIndex="1001"
                            className={classNames(styles.focusActiveStepHelper, 'focus-active-step-helper')}
                        />
                    </Portal>
                )}
                {isShow && overlay && !!state.steps.length && (
                    <Portal>
                        <div
                            className={classNames(styles.overlay, 'guide-overlay')}
                            style={zIndex && { zIndex }}
                            onClick={handleOverlayClick}
                        />
                    </Portal>
                )}
            </div>
        </GuideContext.Provider>
    );
});

Guide.propTypes = {
    /** 自动开始 */
    autoStart: PropTypes.bool,
    /** 只显示一次 */
    showOnce: PropTypes.bool,
    /** 配合 showOnce 使用, 做本地存储 */
    localStorageKey: PropTypes.string,
    /** 是否显示遮罩层 */
    overlay: PropTypes.bool,
    /** zIndex 值, 避免被其他元素覆盖 */
    zIndex: PropTypes.number,
    /** 遮罩层是否可点击 */
    overlayClickNext: PropTypes.bool,
    /** 是否开启 esc 按键功能 */
    allowEsc: PropTypes.bool,
    /** 结束按钮的文案 */
    finishText: PropTypes.string,
    /** 下一步按钮的文案 */
    nextText: PropTypes.string,
    /** 跳过按钮的文案 */
    skipText: PropTypes.string,
    /** 是否隐藏跳过按钮 */
    isShowSkipBtn: PropTypes.bool,
    /** 开始事件 */
    onStart: PropTypes.func,
    /** 下一步事件 */
    onNext: PropTypes.func,
    /** 结束事件 */
    onStop: PropTypes.func
};

Guide.defaultProps = {
    autoStart: true,
    showOnce: false,
    localStorageKey: '',
    overlay: true,
    overlayClickNext: false,
    finishText: '我知道了',
    nextText: '继续了解',
    skipText: '跳过',
    isShowSkipBtn: false,
    zIndex: undefined,
    allowEsc: true,
    onStart: null,
    onNext: null,
    onStop: null
};
Guide.displayName = 'Guide';

export default Guide;
